#pragma once
#include <iostream>
#include <cstdio>
#include <cstdarg>
#include <unordered_map>
#include <ctime>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define Info 1
#define Warning 2
#define Error 3
#define Fatal 4
#define Debug 5

#define BUFSIZE 512

#define SCREEN 5
#define ONEFILE 6
#define CLASSFILE 7

#define LogFile "log.txt"

class Log
{
public:
    Log(int mode = SCREEN)
        :opmode(mode)
    {}

    void enable(int mod)
    {
        opmode = mod;
    }


    void operator()(int grade, const char *format, ...)
    {
        //左半部分  
        //等级
        std::string ret = "[" + hash[grade] + "] ";

        //时间
        time_t timestamp = time(NULL);
        struct tm* _time = localtime(&timestamp);
        ret += "<" + std::to_string(_time->tm_year + 1900) +\
                 "-"+ std::to_string(_time->tm_mon + 1) +\
                 "-"+ std::to_string(_time->tm_mday) +\
                 " " + std::to_string(_time->tm_hour) +\
                 ":" + std::to_string(_time->tm_min) +\
                 ":" + std::to_string(_time->tm_sec) + "> : ";
      
        //右半部分
        va_list s;
        va_start(s, format);
        char buffer[BUFSIZE] = { 0 };
        vsnprintf(buffer, BUFSIZE, format, s);
        va_end(s);

        ret += buffer;
        
        if(opmode == SCREEN)
            std::cout << ret << std::endl;
        else if(opmode == ONEFILE)
            WriteOneFile(ret);
        else
            WriteClassFile(grade, ret);
    }

    void WriteOneFile(std::string& ret, std::string path = ".", std::string filename = LogFile)
    {
        ret += '\n';
        path += '/' + filename;
        int fd = open(path.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0664);
        if(fd < 0)
        {
            perror("open fail");
            exit(1);
        }
        write(fd, ret.c_str(), ret.size());

        close(fd);
    }


    void WriteClassFile(int grade, std::string& ret)
    {
        switch(grade)
        {
        case Info:
            WriteOneFile(ret, "./log", "Info.txt");
            break;
        case Warning:
            WriteOneFile(ret, "./log", "Warn.txt");  
            break;
        case Error:
            WriteOneFile(ret, "./log", "Error.txt");
            break;
        case Fatal:
            WriteOneFile(ret, "./log", "Fatal.txt");
            break;
        default:
            break;
        }
    }


  private:
    std::unordered_map<int, std::string> hash = {
        {Info, "INFO"},
        {Warning, "WARN"},
        {Error, "ERROR"},
        {Fatal, "FATAL"},
        {Debug, "DEBUG"}
    };
    int opmode = SCREEN;
};

//会重复声明，直接在这声明
Log lg;
